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

Piszemy własny graficzny instalator

Dzięki Linuksowi dystrybucja i instalacja oprogramowania wydają się proste, a instalatory zbędne. Nawet Microsoft i Apple, a także Google poszły za ciosem i wprowadziły własne sklepy z aplikacjami. Nie mogę jednak pokryć wszystkich dystrybucji GNU/Linux przez paczki - ba nawet nie mogę wszystkich dystrybucji zainstalować, by przetestować czy paczki działają. W takim wypadku może się przydać instalator. Nie dotyczy to oczywiście bardziej popularnych składników, gdzie społeczność sama przygotowuje paczki.

Właściwy wstęp

Jakiś czas temu napisałem prosty instalator libgreattao. Problem jest jednak taki, że jest to tekstowy instalator. Postanowiłem więc napisać graficzny instalator. Większego problemu nie ma - hierarchia systemu plików GNU/Linux jest dobrze ustandaryzowana(nie dotyczy to dystrybucji Linuksa nie GNU czy dziwnych odchyleń, jak GoboLinux) - nam wystarczy zresztą wiedzieć, gdzie zainstalować biblioteki i katalog z danymi mojego rozwiązania. Jakie będą problemy? Głównym problemem jest to, że większość użytkowników GNU/Linux instaluje system z LiveCD, by nie ściągać zbyt dużo danych. W świecie menadżerów paczek nie jest to problemem, bo brakujące biblioteki zostaną od razu dociągnięte. My jednak chcemy napisać własny instalator. Z tego powodu jestem zmuszony napisać instalator libgreattao w libgreattao.

Trochę technikali

Wybrałem libgreattao, jako iż posiada ono obsługę modułów i automatycznie dobiera odpowiedni moduł do posiadanego środowiska. Jeżeli więc nie mamy biblioteki GTK+, to załaduje się biblioteka Qt. Jeżeli nie mamy środowiska graficznego, to załaduje się interfejs tekstowy. Jest to coś, co jest potrzebne instalatorowi - pokrycie jak największej możliwej ilości konfiguracji.
Ponieważ chcemy napisać instalator libgreattao w libgreattao, to musimy zadbać o to, by odpowiednie biblioteki zostały załadowane w trakcie działania programu, a instalator musi wypakować najpierw specjalną wersję libgreattao i zmienić bieżący katalog na katalog z danymi właśnie wypakowanej wersji libgreattao.
W przypadku instalacji innych programów/bibliotek, moglibyśmy połączyć instalator statycznie z biblioteką lub (raczej jest to możliwe) załadować biblioteką z pamięci, ale libgreattao obsługuje moduły, które muszą być obecne w systemie plików.

Zaczynamy

Pierwsze, co musimy zrobić, to wykorzystać to, czego się nauczyliśmy pisząc tekstowy instalator. Stworzyliśmy wtedy skrypt powłoki, tworzący instalator. Skorzystamy z niego, dokonując paru przeróbek. Musimy także utworzyć nowy instalator - nowy program w C. Trzeba także zmodyfikować Makefile.
Do skryptu tworzącego instalator należy dopisać(tam, gdzie sprzątamy): rm ./tarbal32-without-absolute-path rm ./tarbal64-without-absolute-path rm -r ./tarbal-files-32-without-absolute-path rm -r ./tarbal-files-64-without-absolute-path touch tarbal32 touch tarbal64 touch tarbal32-without-absolute-path touch tarbal64-without-absolute-path Następnie należy dopisać(pod koniec bloku warunkowego): mkdir -p ./tarbal-files-64-without-absolute-path/{share,lib64} make -C ../common clean make -C ../common make -C ../Modules clean make -C ../Modules make -C .. clean make -C .. INSTALLPATH=. make -C .. install INSTALLPATH=./Installer/tarbal-files-64-without-absolute-path cp /usr/lib64/libsell.so* -t ./tarbal-files-64-without-absolute-path/lib64 tar -czf ./tarbal64-without-absolute-path -C tarbal-files-64-without-absolute-path/ . Ważną odnotowania jest linijka: make -C .. INSTALLPATH=. Kropka po installpath oznacza katalog, który będzie bieżący przy wykonaniu konkretnej instrukcji. Oznacza to, że libgreattao będzie szukać danych w bieżącym katalogu.
Podobne zmiany należy wprowadzić odpowiednio dla wersji 32-bitowej.
Należy zwrócić uwagę, że w systemie musimy mieć zainstalowany libsell, który jest zależnością libgreattao i zostanie dodany do archiwum.
Kolejną zmianą jest dodanie treści do Makefile. Oto one: installer-graphical: main_gui.c tarbal32-without-absolute-path tarbal64-without-absolute-path gcc main_gui.c -g -ldl -o tao_installer2 -Wl,--format=binary -Wl,LICENSE -Wl,tarbal32 -Wl,tarbal64 -Wl,tarbal32-without-absolute-path -Wl,tarbal64-without-absolute-path -Wl,--format=default Umożliwi to wygenerowanie instalator graficznego z pliku main_gui.c

Tworzymy właściwy instalator

Na nasze potrzeby, musimy dodać odpowiednie pliki nagłówkowe do naszego programu: #include <libgreattao/tao.h> #include <libgreattao/log.h> #include <dlfcn.h> Dodajemy nagłówki libgreattao, by mieć dostęp do predefiniowanych stałych, mimo iż nie łączymy programu z libgreattao na etapie kompilacji.
Musimy także dodać następującą treść: extern char without_absolute_path_start_32[] asm("_binary_tarbal32_without_absolute_path_start"); extern char without_absolute_path_end_32[] asm("_binary_tarbal32_without_absolute_path_end"); extern char without_absolute_path_start_64[] asm("_binary_tarbal64_without_absolute_path_start"); extern char without_absolute_path_end_64[] asm("_binary_tarbal64_without_absolute_path_end"); char *tarbal_start; char *tarbal_end; void (*tao_initialize2)(char *progname, char *help, int *argv, char **argc); void *(*tao_new_window2)(char *path); void (*tao_add_handler2)(void *window, char *path, void *callback, void *data); void (*tao_set_hint2)(void *window, char *path, char type, void *data); void (*tao_handler_events2)(void); void (*tao_release_window2)(void*); void (*tao_close2)(void); void (*tao_set_text2)(void*, char*, char*); void (*tao_set_modal2)(void*, char*, void*, char); void *window; Dodaliśmy wskaźniki na funkcje importowane z libgreattao. Konieczne jest to, by nazwy tych wskaźników nie pokrywały się z nazwami z naszej biblioteki, gdyż importujemy pliki nagłówkowe naszej biblioteki.
Wydzieliliśmy funkcję unpack, gdyż będziemy wypakowywać dwa archiwa - wersję libgreattao przygotowaną dla instalatora, a także wersję libgreattao do instalacji. Funkcja ta dla każdego archiwum tworzy katalog docelowy i wypakowuje pliki z pomocą polecenia tar.
To były dopiero przygotowania. Teraz należy się wziąć na poważnie. Instalator tak ważnego składnika, jak biblioteki potrzebuje praw administratora. Ponieważ my możemy korzystać z trybu tekstowego, jak i graficznego, to musimy korzystać zarówno z xdg-su, jak i z sudo. W pierwszej kolejności wywołujemy xdg-su, gdyż program może być uruchomiony w środowisku graficznych. Jeżeli uruchomienie xdg-su się nie powiedzie lub zakończy z błędem, to uruchamiamy sudo. By zapobiec uruchomieniu sudo, to po poprawnym wywołaniu xdg-su, kończymy aplikację.
Dla wywołania xdg-su konieczne jest stosowanie kodów ucieczki na ścieżce do naszego programu, gdyż xdg-su przyjmuje jako argument całe polecenie. Funkcji do escapowania stringu nie będę omawiać.

Najważniejszym krokiem jest wypakowanie naszej zmodyfikowanej wersji libgreattao, załadowanie libsell i zmodyfikowanej wersji libgreattao, a następnie utworzenie okna instalatora. Ostatniego kroku nie będę omawiać. Ścieżkę do wypakowanego archiwum libgreattao przechowuje tarbal_path, a jej wartość jest ustawiania przez mkdtemp(tworzy ono tymczasowy katalog). Tę ścieżkę przekazujemy poleceniu unpack. Bardzo istotna jest koleność ładowania bibliotek. Najpierw musimy załadować libsell. Ważne jest też użycie flagi RTLD_GLOBAL, by symbole libsell były dostępne dla libgreattao. Oto kod: library =(char*) malloc(strlen(tarbal_path)+strlen("/libgreattao.so.2") + 1); strcpy(library,tarbal_path); strcat(library, "/libsell.so"); dlopen(library, RTLD_LAZY | RTLD_GLOBAL); free(library); library =(char*) malloc(strlen(tarbal_path)+strlen("/libgreattao.so.2") + 1); strcpy(library,tarbal_path); strcat(library, "/libgreattao.so.2"); void *lib = dlopen(library, RTLD_LAZY); free(library); Bibliotekę należy ładować ścieżką bezwzględną, a więc konieczne jest utworzenie bufora przechowującego ścieżkę do katalogu biblioteki, a także nazwę biblioteki. Ścieżką do katalogu z bibliotekami jest ścieżka wskazywana przez tarbal_path.

Ostatnią rzeczą jest załadowanie adresów odpowiednich procedur. Robimy to tak: tao_initialize2 = dlsym(lib, "tao_initialize");

Wywołanie wskaźnika na procedurę wygląda, jak wywołanie normalnej procedury.

To wszystko, co trzeba zrobić. Jak stworzyć interfejs graficzny w libgreattao nie będę opisywać, ale w libgreattao nie tworzy się interfejsów konkretnego typu. 

linux programowanie

Komentarze

0 nowych
Kaworu   13 #1 19.08.2015 16:19

A jakiś screenshot może, jak to ma finalnie wyglądać/wygląda/działa? ;)

nintyfan   11 #2 19.08.2015 17:08

@Kaworu: Mogę wrzucić screenshot, ale generalnie z użyciem libgreattao programuje się podobnie, jak w wzorcu MVC. Różnica jest taka, że model i kontroler są połączone i jest to aplikacja, a widok generuje libgreattao na podstawie danych dostarczonych przez aplikację, jak ścieżka abstraktu, ścieżka klasy okna, nazwa abstraktu, opis abstraktu, ikona abstraktu, itd.

Nie opisałem, jak dostarczyć informacji do libgreattao - to fakt, bo ostatecznie stwierdziłem, iż można się posłużyć wcześniejszymi artykułami. Może powinienem zmienić tytuł artykułu.

Lubieerror   8 #3 19.08.2015 19:35

@Kaworu: WTW na Linuxie potwierdzone ;)
ps.
Z racji, że już zmusiłem Cię do czytania i przejścia na tą stronę, to przynajmniej życzę miłego dnia ^_^

Kaworu   13 #4 19.08.2015 20:15

@Lubieerror: Ależ, wzajemnie. (Ponabijajmy sobie komentarze?) ;p

Lubieerror   8 #5 22.08.2015 02:58

@Kaworu: Dzięki :)
Niestety ciężko będzie :c
Ostatnio jestem zalatany, a jeszcze ważny dysk zewnętrzny mi padł (wprowadziłem dużo zmian w nim, a przez remont i wyjazdy nie miałem okazji zrobić BU D: )

nintyfan   11 #6 22.08.2015 07:01

Chyba żaden modernator to nie zagląda.

Rozumiem aluzję. Tytuł jest nie w porządku. No ale, kod odpowiedzialny za coś, co wy nazywacie tworzeniem GUI wygląda tak. To raptem tekst i trzy przyciski:
tao_initialize2("Libgreattao installer", "", &argc, argv);

window = tao_new_window2("/desktop/dialogs/question_dialog");

tao_add_handler2(window, "/message", NULL, NULL);
tao_set_hint2(window, "/message", HINT_DESCRIPTION, "Welcome to libgreattao installer. You can read License text. You cannot read License text in this installer after accepting it, so read it carrefully.
Would you like to read libgreattao license text?");

tao_add_handler2(window, "/actions/accept", install,NULL);
tao_add_handler2(window, "/actions/cancel", close_main_window,window);
tao_add_handler2(window, "/actions/license", show_license,NULL);

tao_set_hint2(window, "/actions/license", HINT_DESCRIPTION, "Show license text");

W późniejszych wpisach opiszę, jak zrobić pasek postępu.

Autor edytował komentarz.
awangardowy   7 #7 31.08.2015 02:14

z perspektywy Debiana lepiej ogarnąć jest debianizowanie (proces zamiany oprogramowania w pakiety), bo później łatwiej tym zarządzać poprzez dpkg/apt:
http://www.tecmint.com/dpkg-command-examples/
http://www.tecmint.com/useful-basic-commands-of-apt-get-and-apt-cache-for-packag.../

wiadomo - praktyka przychodzi z czasem, ale później pod względem soft managementu to się robi poezja ;)

dotyczy to też np. instalacji czegoś na serwerze. Np. na serwerze nie ma GUI.

graficznych instalatorów i klikania "dalej" bardzo nie lubię. No i mają mniejsze możliwości.

Autor edytował komentarz.
nintyfan   11 #8 31.08.2015 06:16

@awangardowy: Mój instalator powinien działać również w trybie tekstowym, bo jest napisany w libgreattao. Również nie trzeba wybierać przycisku i klikać dalej, bo możnaby wybrać tzw. tryb powłoki(nie jest to tak naprawdę tryb powłoki). Jedynym problemem jest przekazywanie argumentów - obecnie do sudo przekazujemy tylko nazwę i ścieżkę do programu.

Co do tworzenia pakietów dla Debiana, to starałem się przez OBS, jednak libsdl2-image nie ma w domyślnym repozytoriach.

awangardowy   7 #9 31.08.2015 20:02

@nintyfan:

doceniam, że kodujesz, ale i tak się nie dogadamy. Ostatni mój kontakt z czymkolwiek z rodziny "C" (C++/C/C#/Objective-C) był na studiach. I było to zrobienie jakiegoś prostego zadania w parę godzin. No i jeszcze ten słynny hello world. Do końca życia nie napiszę już żadej linijki dla rodziny c - taka twarda zasada programistyczna. Chyba, że w sytuacji skrajnej, w jakimś projekcie gdy będę miał pistolet przy głowie ;)

z rodziną c jest: za duża konkurencja, projekty są trudne, kasa taka średnia, a na wszelkich możliwych studiach ludzie mają to w programie. A więc wychodzą z uczelni i już coś umieją. W dodatku każdy kto chce programować, to zaczyna googlować i oczywiście wychodzą mu wyniki z "rodziny C". Rodzina C uznawana jest za "podstawę programowania".

A możliwości? Zastanów się czy możesz bez większych komplikacji napisać: aplikację webową (dostępną przez standardową przeglądarkę), portal internetowy, aplikację mobilną dla Androida, aplikację przenośną (na każdym systemie operacyjnym, bez kompilacji, bez bibliotek Microsoftu typu .NET), aplikację skalowalną dla korporacji, prosty i wykonywalny przez interpreter skrypt zarządzający serwerem? Pewnie możesz, ale są lepsze języki do tego: i oczywiście nie są one w rodzinie C.

A jednak bardzo, bardzo mocno trzymam kciuki za "rodzinę c". Oby to było mega popularne, wszyscy utożsamiali to w Polsce z programowaniem, każdy na studiach ostro maglował "rodzinę C", a później szukał w tym pracy, robił domowe projekty, etc.

Mi to jest bardzo na rękę, bo nie będzie konkurencji tam gdzie jest: ciekawiej, szybciej, łatwiej i lepsza kasa, czy potężn(i)e(jsze?) możliwości. Oczywiście wiadomo, że mam na myśli np. język na "j" ;)

to nie jest flame war, tylko subiektywny pogląd.

Autor edytował komentarz.
nintyfan   11 #10 31.08.2015 20:40

@awangardowy: Obsługa(oczywiście podstawowa) protokołu HTTP to nie jest duży wysiłek. W internecie można znaleźć gotowce. Po prostu wysyłasz nagłówek, po czym pustą linijkę i treść strony. Protokół HTTP jest bezstanowy i nie trzeba utrzymywać połączenia, a więc nie martwisz się o zakończenie komunikacji - po prostu zamykasz łącze. Wczytywanie pliku HTML to też nic wielkiego. Nie trzeba go przetwarzać - wystarczy wysłać.