cz.3| Jak to jest być deweloperem aplikacji wieloplatformowej - Deweloper vs Windows
Witam.
Tematem tego wpisu będą moje doświadczenia z instalatorem NSIS podczas budowania instalatora dla aplikacji napisanej w Pythonie.
3. Pakowanie projektu w instalator NSIS
Zrozumienie działania instalatora NSIS zajęło mi sporo czasu, a wskazówki na najbardziej podstawowe pytania były bezużyteczne. Ludzie zadawali masę pytań na forach na jeden nurtujący wszystkich problem - "jak skopiować więcej niż jeden plik".
Niby z NSISa korzysta Pidgin, Winamp i masa innych popularnych aplikacji jednak wydaje mi się, że ten instalator jest tak szeroko wykorzystywany tylko ze względu na ogólny brak alternatyw. Są alternatywy, niektóre z nich płatne, a niektóre mają zbyt małe możliwości.
1) Jak dodać pliki i katalogi projektu rekursywnie
Szukałem godzinami odpowiedzi na to pytanie, jednak trudno było znaleźć konkretną odpowiedź dlatego wymyśliłem prosty skrypt w Pythonie generujący listę plików i katalogów do zarchiwizowania przez instalator.
Listing nsi-paths-build.py
#!/usr/bin/python2 import os commands = "" originalDirectory = "c:\\subget\\build\\exe.win32-2.7" def sortItemsByDirectory(items, directory): directories = list() allItems = list() # first files for item in items: if os.path.isdir(directory+"\\"+item): directories.append(item) else: allItems.append(item) item = "" # and then directories for item in directories: allItems.append(item) return allItems def generateList(directory): global commands, originalDirectory items = os.listdir(directory) for item in items: if os.path.isdir(directory+"\\"+item): #print "\n CreateDirectory "+directory+"\\"+item+"\n SetOutPath "+directory+"\\"+item cd = directory+"\\"+item commands += "\n CreateDirectory "+directory+"\\"+item+"\n SetOutPath "+cd.replace(originalDirectory, '$INSTDIR') generateList(directory+"\\"+item) continue elif os.path.isfile(directory+"\\"+item): #print "\n File "+directory+"\\"+item commands += "\n File "+directory+"\\"+item print "Generating file list..." generateList(originalDirectory) #print fileList print "Opening template..." Template = open("f:\\subget\\windows\installer-template.nsi", "rb") TemplateCode = Template.read().replace("{#INSTALLER_FILES}", commands) Template.close() print "Saving installer script..." Installer = open("f:\\subget\\windows\\installer.nsi", "wb") Installer.write(TemplateCode) Installer.close()
Skrypt ten otwiera szablon skryptu instalatora i zamienia w nim wyrażenie {#INSTALLER_FILES} na listę plików które instalator powinien uwzględnić przy budowaniu.
Fragment listingu installer-template.nsi
OptionsOK: CreateDirectory "$INSTDIR" SetOutPath "$INSTDIR" {#INSTALLER_FILES} ;CopyFiles "c:\subget\build\exe.win32-2.7\*.*" "$INSTDIR" WriteRegStr HKCU "SOFTWARE\Subget" 'Directory' '$INSTDIR' WriteRegStr HKCR "AVIFile\shell\Pobierz napisy\command" "" "$INSTDIR\subget.exe $\"%1$\"" WriteRegStr HKCR "mpgfile\shell\Pobierz napisy\command" "" "$INSTDIR\subget.exe $\"%1$\"" WriteRegStr HKCR "mpegfile\shell\Pobierz napisy\command" "" "$INSTDIR\subget.exe $\"%1$\"" WriteRegStr HKCR "mp4file\shell\Pobierz napisy\command" "" "$INSTDIR\subget.exe $\"%1$\"" WriteRegStr HKCR "3gpfile\shell\Pobierz napisy\command" "" "$INSTDIR\subget.exe $\"%1$\"" SectionEnd
Przykładowe dane generowane przez skrypt nsi‑paths-build.py
File c:\subget\build\exe.win32-2.7\select.pyd File c:\subget\build\exe.win32-2.7\subget.exe File c:\subget\build\exe.win32-2.7\unicodedata.pyd CreateDirectory c:\subget\build\exe.win32-2.7\usr SetOutPath $INSTDIR\usr CreateDirectory c:\subget\build\exe.win32-2.7\usr\share SetOutPath $INSTDIR\usr\share CreateDirectory c:\subget\build\exe.win32-2.7\usr\share\alang SetOutPath $INSTDIR\usr\share\alang CreateDirectory c:\subget\build\exe.win32-2.7\usr\share\alang\python SetOutPath $INSTDIR\usr\share\alang\python File c:\subget\build\exe.win32-2.7\usr\share\alang\python\alang.py File c:\subget\build\exe.win32-2.7\usr\share\alang\python\__init__.py CreateDirectory c:\subget\build\exe.win32-2.7\usr\share\alang\translations SetOutPath $INSTDIR\usr\share\alang\translations CreateDirectory c:\subget\build\exe.win32-2.7\usr\share\alang\translations\english SetOutPath $INSTDIR\usr\share\alang\translations\english File c:\subget\build\exe.win32-2.7\usr\share\alang\translations\english\subget.py File c:\subget\build\exe.win32-2.7\usr\share\alang\translations\english\__init__.py
Teraz pytanie, zapewne retoryczne bo deweloperzy NSIS mi raczej tu nie odpowiedzą ale zadam je - dlaczego NSIS nie obsługuje rekurencyjnego przyłączania całych katalogów? Po co się tyle trudzić żeby zapakować coś w instalator... litości!
W systemach Uniksowych budowanie takiego instalatora jest robotą na maksimum 5 minut, szkoda, że pod Windows człowiek tyle musi się namęczyć.
2) Systemy Uniksowe a instalatory (paczki)
W Arch Linux czy w Gentoo wystarczy w zasadzie pobrać jeden z setek tysięcy gotowych skryptów, zmienić nazwę pakietu, adres źródłowy skąd można pobrać pakiet, wersję i wpisać jedno polecenie które zbuduje pakiet.
- Dokumentacja PKGBUILD w Arch Linux - Polska dokumentacja budowania pakietów dla Arch Linux - Zbiór setek tysięcy gotowych PKGBUILDów z Arch Linux - Ebuildy w Gentoo, nawet po polsku dokumentacja i jaka czytelna!
Jeszcze raz powtórzę pytanie: Dlaczego w Windows trzeba się tle namęczyć? Czy to na prawdę konieczne?