5/11/2013

Python ile geliştirilen uygulamayı paketlemek ve dağıtmak

Geliştirdiğiniz uygulama birkaç betik dosyasından fazla ise ve çalışmak için standart Python kütüphaneleri haricinde bağımlılıklara ihtiyaç duyuyorsa artık bu uygulamayı kullanıcılara dağıtmak için paketlemenin zamanı gelmiş demektir. Ben de bu hafta EN-LinuxClipper uygulamasını paketlemekle geçirdim. Bu yazımda uygulamanın kaynak kodunu distutils ile paketleyip sonra da bunu deb veya rpm'e çevirmekten bahsedeceğim. İlerleyen günlerde Pisi Linux'un yayınlanmasının ardından bir de pisi paketi eklemeyi düşünüyorum.

Öncelikle distutils nedir?
Distutils geliştirdiğimiz Python modüllerini çeşitli sistemler arasında dağıtmak için kullanılan bir kütüphanedir. Bu iş için distutils harici kütüphaneler de vardır örneğin setuptools, distlib, distribute, distutils2 fakat bunlar içerisinde en yaygın olarak setuptools ve distutils kullanılır. Distutils2 ise yakın gelecekte Python3'e geçilmesiyle beraber varsayılan olarak kullanılacak, içerisinde distutils ve setuptools'un güzel özelliklerini barındıracak olan kütüphanedir.

setuptools yerine distutils seçmemin öncelikli nedeni deb ve rpm paketi oluşturmada sağladığı kolaylık ve daha sonraki bir yazıda detaylıca bahsedeceğim C eklentilerini kolayca derleyip bunları Python ile kullanmayı sağlaması.



Nasıl bir dizin yapısı oluşturmalıyız?
Öncelikle yazdığımız Python uygulamasının bir Python modülü gibi davranması gerekiyor, Python modülü gibi davranmaktan kastım uygulamamızla ilgili tüm .py dosyalarını bir "uygulama" dizininde toplayıp içerisinde boş bir __init__.py dosyası oluşturuyoruz, Python'un bir klasörü modül olarak görebilmesi için bu dosyaya ihtiyaç duyuyoruz. Dizinimiz içerisinde bir tane Python modülü olmak zorunda değil "uygulama" "paket2" şeklinde birden fazla modülümüz olabileceği gibi "uygulama1/altuygulama/deneme" gibi bir modüle ait alt modüller de oluşturabiliriz, her dizinin içerisinde __init__.py dosyası olduğu sürece Python hepsini tanıyacaktır.

Dosyayı oluşturduktan sonra artık uygulamamız bir modül haline geldi bunu test etmek için "uygulama" dizininin bulunduğu konumda bir Python kabuğu başlatıp "import uygulama" diyebilirsiniz ya da "from uygulama.altuygulama import deneme; deneme.main()" şeklinde uygulamanızı çalıştıracak fonksiyon ne ise onu çağırıp uygulamanın çalışıp çalışmadığını da test etmeniz faydalı olacaktır. Aynı zamanda uygulamayı başlatan bu ufak betiği paket dizine bin/uygulama olarak kaydetmemiz gerekiyor, daha sonra setup.py içerisinde belirteceğimiz gibi bu betik {prefix}/bin içerisine atılacak. Bu betiğe bir örnek.

Eğer modülün sadece bu konumda değil herhangi bir konumda Python tarafından bulunmasını ve kullanmasını istiyorsak bu "uygulama" dizinini Debian tabanlı sistemlerde /usr/lib/python2.7/dist-packages, Fedora tabanlı sistemlerde de /usr/lib/python2.7/site-packages içerisine taşımalıyız. Aslında distutils'in temel işlevini de bu şekilde özetleyebiliriz.

Python modülü ve başlatıcı betiğin ardından uygulamada kullandığımız diğer grafik, arayüz, ses dosyaları gibi yardımcı elemanları da /data klasörü içerisine atıyoruz. Dizin ismi /data olmak zorunda değil, bu dizin içerisinde de istediğimiz gibi bir dizin yapısı oluşturabiliriz. Bu konuda da herhangi bir kısıtlama bulunmuyor. Ben şu şekilde basit bir data dizini oluşturdum.

Son olarak bir setup.py dosyası oluşturuyoruz, bu dosyayı var olan başka bir şablonu değiştirerek oluşturabiliriz. Bu dosya içerisindeki kısımlar ne işe yarar? Uygulama ismi, lisansı, sürümü gibi bilgi kısımlarını atlayıp önemli kısımlardan bahsetmekte fayda görüyorum.

scripts: Uygulamamızı çalıştıracak ve {prefix}/bin içerisine atılacak dosyanın konumunu belirtmemizi sağlıyor.

packages: Paketimiz içerisinde bulunan ve dist-packages/site-packages içerisine atılmasını istediğimiz Python modüllerinin ve alt modüllerinin listesi.

data_files: /data içerisindeki veya başka ek dosyaların hangi dizinlere atılacağını belirttiğimiz kısım. (atılacak konum, atılacak dosya(lar) ) şeklinde bir diziden oluşur, bu kısımda atılacak dosyaları elle tek tek yazmak yerine glob.glob() fonksiyonundan yardım alıyoruz bu fonksiyon belirttiğimiz düzenli ifadeye uyan dosyaların listesini bir dizi olarak döndürüyor. Atılacak konumu belirtirken de başta gizli bir {prefix} olduğunu unutmayalım share/icons şeklinde belirttiğimiz dizin /usr/share/icons olacağı gibi /usr/local/icons da olabilir.

cmdclass: Bu kısım bir hook mantığında çalışıyor, distutils paketimizi işlerken hangi aşamada hangi fonksiyonun çağırılacağını belirtiyoruz örneğin build sırasında şu, install sırasında bu fonksiyon çağrılsın gibi. Bu kısımda python-distutils-extra paketinde yer alan yardımcı fonksiyonları bağladık fakat bu fonksiyonları kendimiz de yazabilirdik. Bu fonksiyonlar çeviri dosyalarının derlenmesi, yardım dosyalarının ayarlanması gibi işleri bizim yerimize hallediyor.

Kaynak paketimizi hazırladık. Artık herhangi bir sistemde uygulamamız "sudo python setup.py install" diyerek kurulabilir ve komut satırından "uygulama" diyerek çalıştırılabilir hale geldi.

RPM paketi oluşturmak

Bu kısım oldukça basit paket dizininde "python setup.py bdist_rpm" dediğimizde bir dist dizini oluştuğunu ve içerisinde .rpm dosyasının oluşturuğunu görebiliriz, tabi birkaç ufak detay var. Öncelikle bu işlemi Fedora tabanlı bir sistemde yapmalıyız çünkü yukarıda da belirttiğim gibi Fedora ve Debian Python modüllerini farklı dizinlerde tutuyorlar doğal olarak Debian tabanlı sistemde derleyip rpm olarak paketlersek Fedora'da bu paketi kurduğumuzda Python içerisinden modülümüze ulaşamıyoruz, aslında Debian hem site-packages hem dist-packages dizinindeki modülleri tanıyor, fakat Fedora sadece site-packages kullanıyor.

İkinci nokta ise bu rpm paketin bağımlılıklarını ayarlamak, örneğin benim geliştirdiğim uygulama oauth2 modülü kullanıyor. Bu modül ise Fedora tabanlı sistemlerde python-oauth2 paketinde bulunuyor. Bu kısımda bir Fedora üzerinde hangi paketlere ihtiyacınız olduğunu keşfetmeniz gerek.

Gerekli paket(ler)i tespit ettikten sonra bunu:
python setup.py bdist_rpm --requires="python-oauth2" şeklinde belirtip rpm paketimizi oluşturuyoruz. Sadece bağımlılık değil diğer paket özelliklerini de parametreler ile değiştirebiliriz, parametrelerin listesine bu adresten ulaşabilirsiniz.

DEB paketi oluşturmak

Deb paketi oluşturmak için öncelikle kaynak kodu paketimizin içerisine bir debian dizini oluşturuyoruz. Bu dizinin içindeki dosyaları benzer bir deb paketinden bulup düzenleyebilirsiniz. Bu dizinde öncelikli olan control dosyasıdır. Paketle ilgili temel bilgiler bu dosya içerisinde yer alır örn: bağımlılıklar.

debian klasörünü bir şablondan kendimize göre düzenledikten sonra, kaynak kodu paketimizin ana dizinine dönüp
"dpkg-buildpackage -rfakeroot -uc -us" diyoruz.

-rfakeroot: Paketleme işlemi sırasında dpkg bir install simülasyonu yapıyor ve oluşan dosyaları topluyor diyebiliriz, kurulum işlemi de root yetkileriyle yapılıyor. -r dpkg'ye hangi komut ile root olacağını söylediğimiz kısım yani -rsudo da diyebilirdik. Ama ortada gerçek bir kurulum değil de sadece bir simülasyon olduğu için dpkg'nin fakeroot ile gerçekten root olmadan kendini root sanmasını sağlıyoruz.

-uc -us: bu kısımda oluşturulan sources ve changes dosyalarının gpg ile imzalanmadan oluşturulmasını sağlıyor, sanırım u unsign kısaltması oluyor.

Deb paketimizi de oluşturduk ve dpkg -i paket.deb diyerek kurup dağıtmaya hazırız.

Oluşturulan deb paketlerinin kalitesini ölçmeye yarayan lintian isminde bir uygulama var, bu araç bize paketlerken yaptığımız hataları gösteriyor, örneğin debian/changelog dosyası yanlış biçimlendirilmiş gibi.

Benim EN-LinuxClipper için oluşturduğum paketlere bu adresten erişebilirsiniz.

Saygılarımla

0 yorum:

Yorum Gönder