Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

cz.1| Jak to jest być deweloperem aplikacji wieloplatformowej - Deweloper vs Windows

Witam.

Długo nie pisałem - bo ostatni cały tydzień spędziłem na głowieniu się dlaczego to, dlaczego tamto i sto innych rzeczy nie działa pod systemem Windows.
Wybaczcie za wstęp, ale straciłem na prawdę sporo czasu na przenoszeniu swojej jednej małej aplikacji napisanej w Pythonie na system Windows.

Spis treści:
1. Proces przenoszenia kodu Pythona
2. Ogólny proces paczkowania przez wirtualną maszynę, Tworzenie "exe" z plików Pythona
3. Pakowanie projektu w instalator NSIS
4. Podsumowanie i krótki komentarz

No dobrze, no to zacznę według kolejności.

1. Proces przenoszenia kodu Pythona

Jako, że Python jako interpreter działa natywnie pod systemami opartymi o jądro Linux jak i pod systemami z rodziny Windows (NT) tak więc można wnioskować - "a co tam takiego zależnego od platformy jest" a jednak troszkę jest i to troszkę to za dużo.

Zdaję sobie sprawę, że w C, C++ czy innym kompilowanym języku na pewno jest jeszcze więcej problemów przy przenoszeniu aplikacji ale ja opisuję w tym wypadku Pythona i nie zajmuję się C, C++ czy innym językiem kompilowanym.

1) Ładowanie wtyczek, plików językowych

W zasadzie nic trudnego, wystarczy dodać do istniejących odwołań do systemu plików po prostu prefix zależny od systemu operacyjnego który będzie wyglądać mniej więcej tak:

if os.name == "nt": self.prefix = "c:/Program Files/Aplikacja" else self.prefix = ""

Wtedy odwołania bedą wyglądać mniej więcej tak: self.window.set_icon_from_file(self.prefix+"/usr/share/aplikacja/icons/window.png")

Jednak sprawa się bardzo komplikuje jeżeli nie wiemy gdzie aplikacja będzie się znajdować.

Tak więc musimy iść na sam koniec procesu przenoszenia aplikacji na platformę Windows i zbudować instalator, tak zbudować instalator do nie działającej jeszcze aplikacji ponieważ jej działanie jest zależne od instalatora!

Instalator musi utworzyć wpis w rejestrze systemowym z lokalizacją plików aplikacji tak aby nasza aplikacja wiedziała gdzie się znajduje!

Dla instalatora NSIS będzie to taka linijka: WriteRegStr HKCU "SOFTWARE\NazwaAplikacji" 'Directory' '$INSTDIR'

Gdzie:
Directory - nazwa klucza
$INSTDIR - katalog w którym jest zainstalowana aplikacja

Tak więc po zainstalowaniu tworzony jest klucz w rejestrze który wskazuje na katalog w którym użytkownik zainstalował aplikację. Z poziomu aplikacji możemy zatem odczytać ten klucz, aby to zrobić wystarczy mniej więcej coś takiego:

if os.name == "nt": import _winreg try: key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\MojaAplikacja\\', 0, _winreg.KEY_READ) (value, valuetype) = _winreg.QueryValueEx(key, 'Directory') self.prefix = str(value) except WindowsError: print "Cannot find registry key HKEY_CURRENT_USER\Software\MojaAplikacja\Directory, exiting." sys.exit(2)

Brzmi skomplikowanie? Teraz jak to działa w systemach Uniksowych

W systemach Uniksowych znajdują się katalogi /usr/bin, /usr/share, /usr/lib, /etc, $HOME, /tmp i wiele innych, służą one do tego aby segregować instalowaną treść.

/usr/bin - pliki wykonywalne (binarne lub skryptowe) /usr/lib - biblioteki, wtyczki i wszystko co się ładuje dynamicznie /usr/share - obrazki, ikony, dokumentacje, tłumaczenia itp. /etc - pliki konfiguracyjne które są obejmowane ochroną nadpisania podczas aktualizacji systemu, istnieją specjalne narzędzia aby je aktualizować bez żadnych strat $HOME - katalog domowy użytkownika, można w nim zapisać spersonalizowaną konfigurację programu dla danego użytkownika /tmp - katalog tymczasowy, przykładowo program archiwizujący dane może tam tworzyć archiwum a następnie je przenieść do katalogu wybranego przez użytkownika

Budowanie pakietu dla poszczególnych systemów Uniksowych jest bardzo proste, i sprowadza się do edycji jednego pliku oraz wykonania odpowiedniego polecenia które zrobi wszystko za nas.

Dzięki temu, że menadżer pakietów zapamiętuje jakie akcje wykonuje podczas instalacji pakietu jest później w stanie odwrócić zmiany - czyli możliwe jest usunięcie pakietu bez tworzenia deinstalatora.

Przykładowe tworzenie paczki w Arch Linux:

1> Tworzymy katalog z nazwą aplikacji, a w nim plik PKGBUILD z zawartością przykładowo:

pkgname=nazwaaplikacji-git pkgver=0.6 # wersja pkgrel=1 # numer kompilacji paczki dla aktualnej wersji pkgdesc="Opis naszej aplikacji" arch=('i686' 'x86_64') # dostępne platformy url="http://dobreprogramy.pl" # adres url license=('GPL') # licencja depends=('git' 'alang-py' 'python' 'pygtk') # zależności, czyli to co zostanie zainstalowane automatycznie przez menadżer pakietów za nas makedepends=('git') # zależności do samego zbudowania paczki provides=('nazwaaplikacji-git') # paczka zastępuje inną paczkę? conflicts=('nazwaaplikacji') # paczka konfliktuje z inną paczką? # adres url do pobrania z GIT, można także pobrać z archiwum, SVN czy innego źródła, ja wybrałem GIT _gitroot="git://github.com/webnull/nazwaaplikacji.git" _gitname="nazwaaplikacji" build() { cd "$srcdir" if [ -d $_gitname ] ; then cd $_gitname && git pull origin msg "Zaaktualizowano lokalne pliki do najnowszej wersji" else git clone $_gitroot $_gitname fi # czyszczenie katalogu budowania rm -rf "$srcdir/$_gitname-build" git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build" ./configure -parametr1 -parametr2 make # kopiowanie plików wyjściowych do katalogu z danymi paczki cp "$srcdir/mojaaplikacja/usr" "$srcdir/../pkg/usr" -R }

2> Następnie wywołujemy makepkg i za powiedzmy 10 sekund mamy gotową paczkę do zainstalowania którą po zainstalowaniu można jeszcze odinstalować choć nie tworzyliśmy dla niej odinstalatora, proste prawda?

2) Odczyt i zapis plików

Przez kilka godzin dochodziłem do tego dlaczego Windows pozwala odczytać tylko ok. 1/100 zawartości pliku zamiast całości.

Problemem okazało się najwyraźniej kodowanie bo otwierałem pliki w których były polskie ogonki.

Jak widać Linux poradził sobie z ogonkami zakodowanymi w windows-1250 bez problemów, a sam Windows miał problemy ze swoim genialnym kodowaniem.

Rozwiązanie było proste, ale ciężko mi było na nie wpaść przez kilka godzin - a mianowicie należy otwierać pliki w trybie binarnym czyli np. "rb" a nie w zwykłym trybie "r". Linux radzi sobie z ogonkami w obydwóch trybach bez żadnych problemów dlatego błędu nie potrafiłem wykryć.

Zatem jak ktoś mi nie wierzy to zapraszam do testów, proszę spróbować pliki z napisami filmowymi które w 90% są kodowane w windows-1250.

#!/usr/bin/python2 # kompatybilny z Linux i Windows fileHandler = open("tekst.txt", "rb") contents = fileHandler.read() fileHandler.close() print str(len(contents))

#!/usr/bin/python2 # kompatybilny z Linux fileHandler = open("tekst.txt", "r") contents = fileHandler.read() fileHandler.close() print str(len(contents))

3) Wywołania systemowe w razie braku bibliotek

W Pythonie problemem było rozpakowanie pliku skompresowanego przy użyciu 7zip, dlatego postanowiłem użyć zewnętrznego programu /usr/bin/7z (Linux) oraz 7za.exe (Windows).

Pod Linuksem sprawa jest o tyle banalna, że w zależnościach paczki dodajemy wymagany pakiet p7zip i menadżer pakietów sam to zainstaluje - no ba, pakiet znajduje się w każdym systemie Linuksowym więc problemu nie ma.

W Windows trzeba spakować plik 7za.exe do katalogu z aplikacją, a co jeśli wyjdzie aktualizacja do tego pliku? - A co jeśli licencja nie pozwala? Pełno problemów i ograniczeń, pod Windows świat staje się taki skomplikowany...

Dziwny problem z os.system i subprocess

Gdy próbowałem wywołać pod os.system pewne polecenie pod Windows to zwracało mi komunikat w stylu "C:\program" nie ma takiego pliku lub katalogu jednak w subprocess to samo wyrażenie nie powodowało błędu.

# Podobny przykład działa pod Linuksem, ale nie pod Windowsem os.system("\"c:\\katalog z spacja\\7za.exe\" --parametry --otworz archiwum.7z > zapis-do-pliku.txt")

# Podobny przykład działa pdo Windowsem, nie testowany pod Linuksem subprocess.call("\"c:\\katalog z spacja\\7za.exe\" --parametry --otworz archiwum.7z > zapis-do-pliku.txt", shell=True, bufsize=1)

Skoro adres do pliku w którym po drodze znajduje się spacja był wzięty w cudzysłów to system nie powinien mieć problemów z odnalezieniem pliku - a jednak miał.

Ciąg dalszy nastąpi według spisu treści... 

Komentarze

0 nowych
StawikPiast   11 #1 13.07.2011 14:40

coz, webnull, a ty znowu mylisz pojecia. To nie wina Windows ze python jest uposledzony a pythona. operacje o ktorych piszesz w VisualStudio to proscizna. Wiec nie win systemu za to ze ktos inny skopal do niego narzedzie.

otwieranie plikow, instalatory itp to nic trudnego, tylko trzeba miec odpowiednie narzedzia a jak widac ty z twoja wiedza nie nadajecie sie do tego.

Ryan   15 #2 13.07.2011 15:27

1. Nie umieszcza się rzeczy w HKCU\Software\MojaAplikacja tylko w HKCU\Software\MojaFirma\MojaAplikacja. Masz samozaparcie, żeby nie robić syfu w /etc/, ale śmiecenie w rejestrze staje się jakoś magicznie ok.

2. Rejestr jest zbędny, w końcu Python pozwala na wywołanie os.path.dirname(__file__) (lub sys.executable, jeśli to EXE a nie .py). Sam skomplikowałeś sobie sprawę i wyciągasz z tego jakieś absurdalne wnioski.

3. Nie, PKGBUILD nie jest prosty, czytelny ani wygodny, jeśli się go nie używało wcześniej, albo nie siedzi z dokumentacją. Rozumiem fetysz plików (sam kiedyś taki miałem), ale to nie jest żadna obiektywnie wygodna rzecz.

4. CP-1250? Dlaczego używasz czegoś, czego nikt pod Windows nie używa od 12 lat? Oczywiście z miejsca wydaje Ci się, że Twoje braki wiedzowe są równoznaczne z zasysaniem Windy. Norma.

5. Po grzyba 7zip, skoro Python oferuje ZLIBa?

Zamiast rozwiązać problemy we właściwy platformie sposób, postanowiłeś użyć rozwiązań Linuksowych (lub kompletnie wydumanych przez siebie rozwiązań) i dziwisz się, że coś jest przesadnie skomplikowane. No, jest, bo rozwiązujesz nie tylko oryginalny problem, ale i problemy wynikające z błędnych rozwiązań, które sobie narzuciłeś. Z takim podejściem wyleciałbym z każdej pracy (jeśli bym jakąkolwiek znalazł).

webnull   9 #3 13.07.2011 16:02

@Ryan
"4. CP-1250? Dlaczego używasz czegoś, czego nikt pod Windows nie używa od 12 lat? Oczywiście z miejsca wydaje Ci się, że Twoje braki wiedzowe są równoznaczne z zasysaniem Windy. Norma. "

Widocznie 90% napisów filmowych w sieci jest w tym kodowane, cóż mam z tym zrobić?

"3. Nie, PKGBUILD nie jest prosty, czytelny ani wygodny, jeśli się go nie używało wcześniej, albo nie siedzi z dokumentacją. Rozumiem fetysz plików (sam kiedyś taki miałem), ale to nie jest żadna obiektywnie wygodna rzecz. "

PKGBUILD jest prosty, bo ogranicza się tylko do podstawienia zmiennych co w przypadku Windows jest dużo bardziej skomplikowane.

"5. Po grzyba 7zip, skoro Python oferuje ZLIBa? "

Nie wiedziałem, że 7zip jest obsługiwany przez zlib.

"1. Nie umieszcza się rzeczy w HKCU\Software\MojaAplikacja tylko w HKCU\Software\MojaFirma\MojaAplikacja. Masz samozaparcie, żeby nie robić syfu w /etc/, ale śmiecenie w rejestrze staje się jakoś magicznie ok. "

Uważam, że nie powinno się sortować oprogramowania według firmy bo nie tylko firmy tworzą oprogramowanie.

"2. Rejestr jest zbędny, w końcu Python pozwala na wywołanie os.path.dirname(__file__) (lub sys.executable, jeśli to EXE a nie .py). Sam skomplikowałeś sobie sprawę i wyciągasz z tego jakieś absurdalne wnioski. "

Jak na pierwszą aplikację którą napisałem w Pythonie dla Windows nie wiedziałem jeszcze o takim rozwiązaniu.

Ryan   15 #4 13.07.2011 17:20

4. Jeśli A) to program do napisów, to problem z kodowaniem nie jest typowy dla Windows; jeśli B) to nie program dla napisów, to nie rozumiem co to za argument. Ale jeśli już musisz pracować z archaicznymi stronami kodowymi, to użyj czegoś w stylu unicode(your_data, "iso-8859-2") i wewnętrznie operuj na unicode. Dawno minęły czasy, kiedy miejsce "marnowane" przez unicode miało znaczenie.

3. Co to są "podstawowe opcje"? I dlaczego winą Windowsa jest działanie instalatora Nullsoftu, wyjaśnisz mi?

1. Uważam, że Twoje zdanie nie ma znaczenia, jeśli są wytyczne programowania dla Windows. Jeśli nie chcesz używać nazwy firmy, użyj nazwisko lub nicka, ale NIE umieszczaj opcji w kluczu bezpośrednio pod HKCU\Software. Moje zdanie na temat /etc/ jest takie, że to kosmiczny śmietnik. Ale nie zamierzam łamać reguł tylko dlatego, że się z nimi nie zgadzam.

2. O wielu rzeczach nie wiedziałeś, ale nie przeszkadzało Ci to wyciągnąć wnioski na temat programowania pod Windows. Pomijam też to, że wspomniana funkcja jest "platform agnostic".

  #5 13.07.2011 21:02

Dlaczego nie możesz operować na ścieżkach bezwzględnych. Poczytaj tutoriale o operacjach na ścieżkach w pytonie (zwłaszcza moduł os.path ). Po co wymyślać koło na nowo. Python ma bardzo dobre mechanizmy do wiloplatformowości . Po to są moduł "os" (złaszcza os) i "sys" aby uniknąć takich problemów.

webnull   9 #6 13.07.2011 22:21

@pymarek (niezalogowany) | 13.07.2011 21:02
Pierwszy raz programuję wieloplatformowo, i ten pierwszy raz zawsze jest najgorszy bo o pewnych rozwiązaniach się po prostu nie wie i wymyśla się nowe ;-)

przemo_li   11 #7 14.07.2011 19:11

@Ryan
Jeśli webnull operuje na zewnętrzych danych testowych, wtedy poblem z otwarciem kodowania made by MS w OSie made by MS jest ... czarnym humorem.

Owszem wewnętrznie można przekształcić na unicode, ale najpierw trzeba otworzyć ten plik :).

Tak samo jeśli musi korzystać z zewnętrznych archiwów 7zip to ZLIB mu się na nic nie przyda. :(

@webnull
os.path.dirname(__file__) <- wpadka, to jest prosty Pythonowy idiom.

Ryan   15 #8 14.07.2011 20:15

Tyle że skompilowany do exe skrypt z __file__ nie zadziała. ;>

  #9 15.07.2011 13:17

@Ryan
Zamiast __file__ można użyć
os.path.dirname(sys.path[0])
os.path.abspath(".")
os.path.realpath(".")

Przy czym pierwsze rozwiązanie jest trochę ryzykowne.
Inne przydatne cosie: sys.argv, os.getcwd ,os.listdir(".")

@webnull
O ile pamiętam spacje w nazwie katalogów zapisujemy "\ " a nie " " ale dawno nie sprawdzałem. Oczywiście najlepiej operować na ścieżce względnej i ominąć problem.

Ryan   15 #10 15.07.2011 14:40

@pymarek: Wiem, że się da inaczej, przecież napisałem o tym w pierwszym komentarzu. ;)

webnull   9 #11 16.07.2011 10:35

@pymarek (niezalogowany) | 15.07.2011 13:17
Spacji normalnie nie używam i staram się je zamieniać na "\ ", ale są one używane właśnie głównie w środowisku Windows. w Uniksach rzadko się widzi spacje w katalogach ;>

webnull   9 #12 16.07.2011 10:37

@przemo_li | 14.07.2011 19:11
Może i jest bardzo proste to wszystko ale jak ktoś programuje dla Uniksów to taka informacja jest mu zupełnie nie potrzebna dlatego, że z góry wie, że aplikacja znajduje się tu i tu.

Ryan   15 #13 16.07.2011 14:24

Jak mam na dzielonym kompie, bez odpowiednich uprawnień odpalać własne aplikacje z katalogu innego, niż domowy? I skąd dokładnie z ~ będę uruchamiał program? Pisanie, że w *niksach zawsze wiadomo gdzie znajduje się program nie jest ani odrobinę prawdą.

  #14 17.07.2011 17:46

Za to, co Intel i IBM zrobiły z architekturą x86 oraz wymuszenie kompatybilności wstecznej życzę tym z-piekła-rodem firmom wszystkiego najgorszego !
Microsoft *musiał* się dostosować z softwarem do architektury x86, bo innej zwyczajnie nie było.

webnull   9 #15 17.07.2011 18:15

@Ryan (redakcja) | 16.07.2011 14:24
Jak to gdzie? - w /usr/bin znajdują się pliki wykonywalne, reszta tam gdzie powinna.

$ ls /usr/bin |wc -l
2939

Jak widać tyle oprogramowania mam zainstalowanego w systemie :-)

@iSalt0 | 17.07.2011 17:46
Nie bardzo rozumiem o co Ci chodzi.

  #16 17.07.2011 19:06

-> webnull
1/ sztuczna segmentacja pamięci i nienaturalne tryby adresowania, EMS, XMS
2/ czarna magia z trybem chronionym
3/ 16-bitowy adres, 8-bitowe dane
4/ kompatybilność wsteczna z takimi rozwiązaniami do Pentium włącznie

Aby zminimalizować koszt wykonania prostej operacji arytmetycznej w JavaScript, trzeba dokonać takiego zapisu:
i+++-+++j
Takie "kwiatki" zawdzięczamy Intelowi i IBM.

Ryan   15 #17 18.07.2011 17:15

@webnull: Nie zrozumieliśmy się. Ktoś udostępnił mi shella, piszę na nim kawałek kodu. Nie dodaję elfów do /bin/ ani /usr/bin/, bo to kod rozwojowy, prawdopodobnie nie działa (możliwe nawet, że w takiej postaci leży sobie kilka dni) i zachwaszczanie nim obszaru spoza katalogu domowego to pierwszorzędne śmiecenie. Skoro program nie jest w /bin/ ani w /usr/bin/, to skąd ma wiedzieć gdzie się znajduje, jeśli nie dzięki importowi os?

@iSalt0: A jaki to ma związek z wpisem webnulla? :S

dingo12   3 #18 19.07.2011 20:32

Mnie najbardziej zdziwiło odczytywanie danych z pliku. Jeden atrybut 'b' zaważył na wszystkim :O. Ale ważne, że da się to zrobić nie robiąc ifa :P.

  #19 31.07.2012 10:56

@webnull

Czepiają się tu Ciebie ale trochę racji mają. Też tylko linuksa używam na codzień, ale Ty starasz się tu za wszelką cenę pokazać, że to Windows jest zły. Często chodzi po prostu o odpowiednie podejście do problemu.